﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

using SlimDX;
using SlimDX.Direct3D9;
using System.IO;

namespace DiverseMeshTools
{
    public partial class Main : Form
    {
        public Main()
        {
            InitializeComponent();
        }

        private void buttonInput_Click(object sender, EventArgs e)
        {
            DialogResult dialogResult = DialogResult.None;
            bool load = false;

            try
            {
                OpenFileDialog openFileDialog = new OpenFileDialog();
                openFileDialog.Filter = "DirectX9 Mesh (*.x)|*.x";

                dialogResult = openFileDialog.ShowDialog(this);

                switch (dialogResult)
                {
                    case DialogResult.OK:
                        load = true;
                        break;

                    default:
                        load = false;
                        break;
                }

                if (load)
                {
                    if (File.Exists(openFileDialog.FileName))
                    {
                        textInput.Text = openFileDialog.FileName;

                        dialogResult = MessageBox.Show(this, "Do you want me to set the output path to the same file? (Overwrite source file?)", "Output path settings ...", MessageBoxButtons.YesNo);

                        if (dialogResult == DialogResult.Yes)
                        {
                            textOutput.Text = openFileDialog.FileName;
                        }
                    }
                    else
                    {
                        MessageBox.Show(this, "Mesh could not be loaded.", "Error", MessageBoxButtons.OK);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, "An error occured:\n" + ex.Message, "Error", MessageBoxButtons.OK);
            }         
        }

        private void buttonOutput_Click(object sender, EventArgs e)
        {
            DialogResult dialogResult = DialogResult.None;
            bool save = false;

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "DirectX9 Mesh (*.x)|*.x";

            dialogResult = saveFileDialog.ShowDialog(this);

            switch (dialogResult)
            {
                case DialogResult.OK:
                    save = true;
                    break;

                default:
                    save = false;
                    break;
            }

            if (save)
            {
                textOutput.Text = saveFileDialog.FileName;
            }
        }

        private void buttonText_Click(object sender, EventArgs e)
        {
            Save(XFileFormat.Text);
        }

        private void buttonCompressed_Click(object sender, EventArgs e)
        {
            Save(XFileFormat.Compressed);
        }

        private void buttonBinary_Click(object sender, EventArgs e)
        {
            Save(XFileFormat.Binary);
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void Save(XFileFormat xFileFormat)
        {
            try
            {
                if (textInput.Text != null && File.Exists(textInput.Text) && textOutput.Text != null)
                {
                    // Create Device
                    Direct3D direct3D = new Direct3D();
                    Device device = new Device(direct3D, 0, DeviceType.Hardware, this.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters());

                    // Load & DX otimize mesh
                    Mesh mesh = Mesh.FromFile(device, textInput.Text, MeshFlags.Managed);
                    mesh.OptimizeInPlace(MeshOptimizeFlags.VertexCache | MeshOptimizeFlags.Compact | MeshOptimizeFlags.AttributeSort);

                    // TODO: Transform
                    //mesh = D3D9MeshTools.Transform(mesh, GetTransform());
                    
                    // Optimize with Mesh Tools
                    Mesh optimized = D3D9MeshTools.Optimize(mesh);
                    optimized.OptimizeInPlace(MeshOptimizeFlags.VertexCache | MeshOptimizeFlags.Compact | MeshOptimizeFlags.AttributeSort);

                    // Save the optimized mesh
                    Mesh.ToXFile(optimized, textOutput.Text, xFileFormat);

                    #region Free memory
                    mesh.Dispose();
                    mesh = null;

                    optimized.Dispose();
                    optimized = null;

                    device.Dispose();
                    device = null;

                    direct3D.Dispose();
                    direct3D = null;

                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    #endregion

                    MessageBox.Show(this, "Mesh processed.", "Success", MessageBoxButtons.OK);
                }
                else
                {
                    MessageBox.Show(this, "Mesh could not be written.", "Error", MessageBoxButtons.OK);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, "An error occured:\n" + ex.Message, "Error", MessageBoxButtons.OK);
            }
        }

        private Matrix GetTransform()
        {
            Matrix transformation = Matrix.Identity;

            if (checkScaleAll.CheckState == CheckState.Checked && IsNumeric(textScaleAll.Text))
            {
                transformation = transformation * Matrix.Scaling(new Vector3(Convert.ToSingle(textScaleAll.Text)));
            }

            if (checkScaleX.CheckState == CheckState.Checked && IsNumeric(textScaleX.Text))
            {
                transformation = transformation * Matrix.Scaling(Convert.ToSingle(textScaleX.Text), 1f, 1f);
            }

            if (checkScaleY.CheckState == CheckState.Checked && IsNumeric(textScaleY.Text))
            {
                transformation = transformation * Matrix.Scaling(1f, Convert.ToSingle(textScaleY.Text), 1f);
            }

            if (checkScaleZ.CheckState == CheckState.Checked && IsNumeric(textScaleZ.Text))
            {
                transformation = transformation * Matrix.Scaling(1f, 1f, Convert.ToSingle(textScaleZ.Text));
            }

            if (checkRotationX.CheckState == CheckState.Checked && IsNumeric(textRotateX.Text))
            {
                transformation = transformation * Matrix.RotationX(Convert.ToSingle(textRotateX.Text));
            }

            if (checkRotationY.CheckState == CheckState.Checked && IsNumeric(textRotateY.Text))
            {
                transformation = transformation * Matrix.RotationY(Convert.ToSingle(textRotateY.Text));
            }

            if (checkRotationZ.CheckState == CheckState.Checked && IsNumeric(textRotateZ.Text))
            {
                transformation = transformation * Matrix.RotationZ(Convert.ToSingle(textRotateZ.Text));
            }

            if (checkMoveX.CheckState == CheckState.Checked && IsNumeric(textMoveX.Text))
            {
                transformation = transformation * Matrix.Translation(Convert.ToSingle(textMoveX.Text), 0f, 0f);
            }

            if (checkMoveY.CheckState == CheckState.Checked && IsNumeric(textMoveY.Text))
            {
                transformation = transformation * Matrix.Translation(0f, Convert.ToSingle(textMoveY.Text), 0f);
            }

            if (checkMoveZ.CheckState == CheckState.Checked && IsNumeric(textMoveZ.Text))
            {
                transformation = transformation * Matrix.Translation(0f, 0f, Convert.ToSingle(textMoveZ.Text));
            }

            return transformation;
        }

        private bool IsNumeric(string text)
        { 
            float test;
            return Single.TryParse(text, out test);
        }

        #region Transformation Input
        private void checkRotationX_CheckedChanged(object sender, EventArgs e)
        {
            textRotateX.ReadOnly = checkRotationX.CheckState != CheckState.Checked;
        }

        private void checkRotationY_CheckedChanged(object sender, EventArgs e)
        {
            textRotateY.ReadOnly = checkRotationY.CheckState != CheckState.Checked;
        }

        private void checkRotationZ_CheckedChanged(object sender, EventArgs e)
        {
            textRotateZ.ReadOnly = checkRotationZ.CheckState != CheckState.Checked;
        }

        private void checkMoveX_CheckedChanged(object sender, EventArgs e)
        {
            textMoveX.ReadOnly = checkMoveX.CheckState != CheckState.Checked;
        }

        private void checkMoveY_CheckedChanged(object sender, EventArgs e)
        {
            textMoveY.ReadOnly = checkMoveY.CheckState != CheckState.Checked;
        }

        private void checkMoveZ_CheckedChanged(object sender, EventArgs e)
        {
            textMoveZ.ReadOnly = checkMoveZ.CheckState != CheckState.Checked;
        }

        private void checkScaleX_CheckedChanged(object sender, EventArgs e)
        {
            textScaleX.ReadOnly = checkScaleX.CheckState != CheckState.Checked;
        }

        private void checkScaleY_CheckedChanged(object sender, EventArgs e)
        {
            textScaleY.ReadOnly = checkScaleY.CheckState != CheckState.Checked;
        }

        private void checkScaleZ_CheckedChanged(object sender, EventArgs e)
        {
            textScaleZ.ReadOnly = checkScaleZ.CheckState != CheckState.Checked;
        }

        private void checkScaleAll_CheckedChanged(object sender, EventArgs e)
        {
            textScaleAll.ReadOnly = checkScaleAll.CheckState != CheckState.Checked;
        }
        #endregion

        private void buttonPreview_Click(object sender, EventArgs e)
        {
            if (textInput.Text != null && File.Exists(textInput.Text))
            {
                DialogResult result = MessageBox.Show(this, "Skip optimization in preview?", "Optimization?", MessageBoxButtons.YesNoCancel);
                bool skipOptimization = false;

                switch (result)
                { 
                    case DialogResult.Yes:
                        skipOptimization = true;
                        break;

                    case DialogResult.No:
                        skipOptimization = false;
                        break;

                    default:
                        return;
                }

                Preview preview = new Preview(textInput.Text, GetTransform(), skipOptimization);
                preview.ShowDialog(this);

                #region Dispose
                if (preview != null)
                {
                    if (!preview.Disposing)
                    {
                        preview.Dispose();
                    }

                    preview = null;
                }
                #endregion
            }
            else
            {
                MessageBox.Show(this, "Mesh not found.", "Error", MessageBoxButtons.OK);
            }
        }
    }
}
